#include <stdint.h>
#include <stdbool.h>

#include "driver_system.h"
#include "driver_pwm.h"

volatile struct pwm_reg_t *pwm_reg = (volatile struct pwm_reg_t *)PWM_BASE;

void pwm_init(enum pwm_channel_t channel, enum pwm_mode_t mode, uint32_t scale,
    uint32_t period,uint32_t pos_start_offset, uint32_t pos_dur)
{
    uint32_t num;

    system_regs->misc.gpio_apb_clk_en = 1;
    system_regs->clk_gate.gpio_clk_en = 1;

    pwm_reg->enable.en &= ~(1<<channel);            //write 0 to disable PWM 
    pwm_reg->out_enable.en &= ~(1<<channel);        //write 0 to enable output PAD
    pwm_reg->sync_update.update |= (1<<channel);

    if(mode == PWM_DAC_LEVELOUT_MODE){
        pwm_reg->mode.mode |= (1<<channel);
        pwm_reg->dac_div_cnt[channel].cnt = scale;
        pwm_reg->edge[channel].p.dac_levelout = pos_dur;
    }else{
        pwm_reg->mode.mode &= ~(1<<channel);
        pwm_reg->config[channel].prescale = scale;
        pwm_reg->config[channel].period = period;
        pwm_reg->edge[channel].pos_edge = pos_start_offset;
        pwm_reg->edge[channel].p.neg_edge = (pos_start_offset + pos_dur)%(period+1);
    }
    
}

void pwm_tach_init(enum pwm_channel_num_t channel,uint8_t scale,bool continue_flag)
{
    pwm_reg->tach.prescale = scale&0x0f;
    pwm_reg->tach_mode.mode = continue_flag;
    pwm_reg->tach_irq_mask.mask = 0xff;
    pwm_reg->tach_mode.en |= channel;
}

uint8_t pwm_read_tach_status()
{
    return pwm_reg->tach_status.status;
}

uint32_t pwm_read_tach_value(enum pwm_channel_t channel)
{
    return pwm_reg->tach_pulse_dur[channel];
}

void clear_pwm_status()
{
    pwm_reg->tach_status.status = 0xff;
}

void pwm_enable_inverter(enum pwm_channel_num_t channel)
{
    pwm_reg->inv.inv|=channel;
}

void pwm_dynamic_update(enum pwm_channel_t channel, enum pwm_mode_t mode, uint32_t scale, uint32_t period,uint32_t pos_start_offset, uint32_t pos_dur)
{
   
    pwm_reg->sync_update.update &= ~(1<<channel);   

    if(mode == PWM_DAC_LEVELOUT_MODE){
        //pwm_reg->mode.mode |= (1<<channel);
       // pwm_reg->dac_div_cnt[channel].cnt = scale;
        pwm_reg->edge[channel].p.dac_levelout = pos_dur;
    }else{
        pwm_reg->config[channel].prescale = scale;
        pwm_reg->config[channel].period = period;
        pwm_reg->edge[channel].pos_edge = pos_start_offset;
        pwm_reg->edge[channel].p.neg_edge = (pos_start_offset + pos_dur)%(period+1);
    }
    //pwm_reg->sync_update.update |= (1<<channel);
}

void pwm_dynamic_update_start(enum pwm_channel_num_t channel)
{
    pwm_reg->sync_update.update |= channel;  
}

void pwm_start(enum pwm_channel_num_t channel)
{

    //REG_PL_WR(PWM_CNT_ENABLE,REG_PL_RD(PWM_CNT_ENABLE)|(1<<channel));
    //REG_PL_WR(PWM_ENABLE,REG_PL_RD(PWM_ENABLE)|(1<<channel));
    pwm_reg->cnt_en.cnt_en |= channel;
    pwm_reg->enable.en |=channel;
}

void pwm_start_channels(uint16_t chnl)
{
    //REG_PL_WR(PWM_CNT_ENABLE,chnl);
    //REG_PL_WR(PWM_ENABLE,chnl);
    pwm_reg->cnt_en.cnt_en = chnl;
    pwm_reg->enable.en = chnl;
}

void pwm_stop_channels(void)
{
    pwm_reg->enable.en = 0;
    while(pwm_reg->sync_update.normal_status);
    pwm_reg->cnt_en.cnt_en = 0;
    pwm_reg->mode.mode = 0;
}

void pwm_stop(enum pwm_channel_t channel)
{
    //REG_PL_WR(PWM_ENABLE,REG_PL_RD(PWM_ENABLE)&(~(1<<channel)));
    pwm_reg->enable.en &=  ~(1<<channel);
    if((pwm_reg->mode.mode&(1<<channel)) == 0){
        while((pwm_reg->sync_update.normal_status)&(1<<channel));
    }
    pwm_reg->mode.mode &= ~(1<<channel);
    pwm_reg->cnt_en.cnt_en &=  ~(1<<channel);
}

void pwm_stretch_enable(enum pwm_channel_t channel,uint8_t val)
{
    //pwm_stop(channel);
    pwm_reg->cnt_en.cnt_en |= (1<<channel);
    #if 0
    REG_PL_WR(PWM_STRETCH,REG_PL_RD(PWM_STRETCH)|(1<<channel));
    if(val){
        REG_PL_WR(PWM_STRETCH_VALUE,REG_PL_RD(PWM_STRETCH_VALUE)|(1<<channel));
    }else{
        REG_PL_WR(PWM_STRETCH_VALUE,REG_PL_RD(PWM_STRETCH_VALUE)&(~(1<<channel)));
    }
    #else
    if(val){
        pwm_reg->stretch_val.val |= (1<<channel);
    }else{
        pwm_reg->stretch_val.val &= ~(1<<channel);
    }
    pwm_reg->stretch.en |= (1<<channel);
    #endif
}

void pwm_stretch_disable(enum pwm_channel_t channel)
{
    pwm_stop(channel);
    //REG_PL_WR(PWM_STRETCH,REG_PL_RD(PWM_STRETCH)&(~(1<<channel)));
    pwm_reg->stretch.en &= ~(1<<channel);
}

void pwm_update(enum pwm_channel_t channel, enum pwm_mode_t mode, uint32_t scale, uint32_t period,uint32_t pos_start_offset, uint32_t pos_dur)
{
    pwm_reg->sync_update.update &= ~(1<<channel);
    if(mode == PWM_DAC_LEVELOUT_MODE){
        pwm_reg->mode.mode |= (1<<channel);
        pwm_reg->dac_div_cnt[channel].cnt = scale;
        pwm_reg->edge[channel].p.dac_levelout = pos_dur;
    }else{
        pwm_reg->mode.mode &= ~(1<<channel);
        pwm_reg->config[channel].prescale = scale;
        pwm_reg->config[channel].period = period;
        pwm_reg->edge[channel].pos_edge = pos_start_offset;
        pwm_reg->edge[channel].p.neg_edge = (pos_start_offset + pos_dur)%(period+1);
    }
    pwm_reg->sync_update.update |= (1<<channel);
}
void pwm_inv(enum pwm_channel_t channel)
{
    uint8_t val = 0;
    if((pwm_reg->inv.inv) & (1<<channel)){
        pwm_reg->inv.inv &= ~(1<<channel);
    }else{
        pwm_reg->inv.inv |= (1<<channel);
    }
}

uint8_t test_pwm_tach_flag = 1;
uint8_t test_pwm_tach_cnt = 0;

void pwm_test()
{
    uint8_t result;
    uint32_t num;
    co_printf("digital_pwm demo\r\n");

    system_regs->misc.pwm_apb_clk_en=1;
    system_set_port_mux(GPIO_PORT_D,GPIO_BIT_0,PORTD0_FUNC_PWM0);
    system_set_port_mux(GPIO_PORT_D,GPIO_BIT_1,PORTD1_FUNC_PWM1);
    system_set_port_mux(GPIO_PORT_D,GPIO_BIT_2,PORTD2_FUNC_PWM2);
    system_set_port_mux(GPIO_PORT_D,GPIO_BIT_3,PORTD3_FUNC_PWM3);
    system_set_port_mux(GPIO_PORT_D,GPIO_BIT_4,PORTD4_FUNC_PWM4);
    system_set_port_mux(GPIO_PORT_D,GPIO_BIT_5,PORTD5_FUNC_PWM5);
    system_set_port_mux(GPIO_PORT_D,GPIO_BIT_6,PORTD6_FUNC_PWM6);

    co_printf("output wave, scale = 2,period = 15,pos = 6,neg = 8\r\n");
    pwm_init(PWM_CHANNEL_4,PWM_NORMAL_MODE,4,15,6,2);
    pwm_init(PWM_CHANNEL_5,PWM_NORMAL_MODE,4,15,6,2);
    pwm_enable_inverter(PWM_CHANNEL_NUM5);
    pwm_start(PWM_CHANNEL_NUM4|PWM_CHANNEL_NUM5);
    co_delay_100us(10000);

    pwm_dynamic_update(PWM_CHANNEL_4,PWM_NORMAL_MODE,4,15,4,6);
    pwm_init(PWM_CHANNEL_2,PWM_NORMAL_MODE,4,15,6,2);
    pwm_start(PWM_CHANNEL_NUM2);
    pwm_dynamic_update_start(PWM_CHANNEL_NUM4);

    //TACH _MODE
#if  0
    pwm_tach_init(PWM_CHANNEL_NUM6,0,true);
    while(1)
    {
        result=pwm_read_tach_status();	
        co_printf("result:%x\r\n",result);
        if(result>0)
        {
            num=pwm_read_tach_value(PWM_CHANNEL_6);
            co_printf("value:%x\r\n",num);

            clear_pwm_status();
        }
    }
#endif

}

#if  0
void pwm_isr(void)
{
    uint8_t status = pwm_reg->tach_status.status;
    co_printf("pwm isr,status = %x\r\n",status);
    for(uint8_t i=0;i<8;i++){
        if(status&(1<<i)){
            co_printf("pwm chan %d isr, cnt = %x\r\n",i,pwm_reg->tach_pulse_dur[i]);
        }
    }
    
    if(test_pwm_tach_flag == 1)
        test_pwm_tach_flag = 0;
    else{
        test_pwm_tach_cnt ++;
        if(test_pwm_tach_cnt >= 5){
            test_pwm_tach_flag = 1;
        }
    }
    
    pwm_reg->tach_status.status = 0xff;
}

#endif 